package cn.com.utils;

import cn.com.bean.ResultResponse;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * http客户端util
 *
 * @author LiuGuodong
 * @date 2024/01/23
 */
@Slf4j
public class HttpClientUtil {

    public static final ConnectionKeepAliveStrategy MY_STRATEGY = (response, context) -> {
        HeaderElementIterator it = new BasicHeaderElementIterator
                (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase
                    ("timeout")) {
                return Long.parseLong(value) * 1000;
            }
        }
        return 60 * 1000;//如果没有约定，则默认定义时长为60s
    };
    private static final String MAX_CONNECTIONS_PROPERTY = "http.maxConnections";
    private static final String KEEP_ALIVE_PROPERTY = "http.keepAlive";
    private static final int DEFAULT_TIMEOUT = 100;

    private static final CredentialsProvider credsProvider = new BasicCredentialsProvider();
    private static final CloseableHttpClient httpclient;
    private static final HttpGet httpget;
    private static final RequestConfig reqestConfig;
    private static final ResponseHandler<String> responseHandler;

    static {
        // 设置系统属性
        System.setProperty(MAX_CONNECTIONS_PROPERTY, "50");
        System.setProperty(KEEP_ALIVE_PROPERTY, "true");

        // 设置CredentialsProvider
        credsProvider.setCredentials(
                new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM),
                new UsernamePasswordCredentials("", ""));

        // 创建HttpClient实例
        httpclient = HttpClients.custom()
                .useSystemProperties()
                .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
                .setDefaultCredentialsProvider(credsProvider)
                .build();

        // 初始化HttpGet
        httpget = new HttpGet();
        // 初始化请求配置
        reqestConfig = RequestConfig.custom()
                .setContentCompressionEnabled(true)
                .setSocketTimeout(DEFAULT_TIMEOUT)
                .setAuthenticationEnabled(true)
                .setConnectionRequestTimeout(DEFAULT_TIMEOUT)
                .setConnectTimeout(DEFAULT_TIMEOUT)
                .build();

        // 设置HttpGet的配置
        httpget.setConfig(reqestConfig);
        // 初始化响应处理器
        responseHandler = new BasicResponseHandler();
    }


    public static String getResultResponse(Response response) {
        StringBuilder result = new StringBuilder();
        String line;
        try (InputStreamReader inReader = new InputStreamReader(response.body().byteStream());
             BufferedReader rd = new BufferedReader(inReader)) {
            while ((line = rd.readLine()) != null) {
                result.append(line);
            }
            //print("Response: " + response.toString());
            //
            //print("Response body: " + result.toString());
            //
            //print("Response code: " + response.code());

            Headers headers = response.headers();
            Set<String> names = headers.names();
            Map<String, String> headersMap = new HashMap<>();
            for (String header : names) {
                headersMap.put(header, headers.get(header));
            }
            //System.out.println("Response headers: " + headersMap);


        } catch (Exception e) {
            System.err.println("Error processing response" + e);
        }

        return result.toString();
    }

    public static String buildUrl(String baseUrl, Map<String, String> parameters) {
        // 将参数拼接到URL中
        String queryString = parameters.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));

        // 构建完整的URL
        String fullUrl = baseUrl + "?" + queryString;

        return fullUrl;
    }

    private static void print(String result) {
        System.out.println(result);
        System.out.println("\n");
    }

    /**
     * http, https post请求，带超时时间
     *
     * @param url         url
     * @param headers     header
     * @param bodyJsonStr body
     * @return ResultResponse
     * @throws Exception 例外
     */
    public static ResultResponse post(String url, Map<String, String> headers, String bodyJsonStr) throws Exception {

        CloseableHttpClient client = getHttpClient();
        HttpPost post = new HttpPost(url);
        // add header
        if (headers != null) {
            headers.forEach(post::addHeader);
        }

        post.setEntity(new StringEntity(bodyJsonStr, StandardCharsets.UTF_8));
        ResultResponse resultResponse = null;
        HttpResponse response = client.execute(post);
        resultResponse = buildResultResponse(response);

        return resultResponse;
    }

    public static ResultResponse delete(String url, Map<String, String> headers, String bodyJsonStr) throws Exception {
        return delete(url, headers);
    }

    /**
     * delete
     */
    public static ResultResponse delete(String url, Map<String, String> headers) throws Exception {
        CloseableHttpClient client = getHttpClient();
        HttpDelete request = new HttpDelete(url);

        // add request header
        if (headers != null) {
            headers.forEach(request::addHeader);
        }
        ResultResponse resultResponse = null;
        HttpResponse response = client.execute(request);
        resultResponse = buildResultResponse(response);
        return resultResponse;
    }

    /**
     * 收到
     *
     * @param url         url
     * @param headers     请求头
     * @param bodyJsonStr body json-str
     * @return {@link ResultResponse}
     * @throws Exception 例外
     */
    public static ResultResponse get(String url, Map<String, String> headers, String bodyJsonStr) throws Exception {
        url = buildParameterUrl(url, bodyJsonStr);
        return get(url, headers);
    }

    /**
     * http, https get
     */
    public static ResultResponse get(String url, Map<String, String> headers) throws Exception {
        CloseableHttpClient client = getHttpClient();
        HttpGet request = new HttpGet(url);

        // add request header
        if (headers != null) {
            headers.forEach(request::addHeader);
        }
        ResultResponse resultResponse = null;
        HttpResponse response = client.execute(request);
        resultResponse = buildResultResponse(response);
        return resultResponse;
    }


    /**
     * put
     */
    public static ResultResponse put(String url, Map<String, String> headers, String bodyJsonStr) throws Exception {

        CloseableHttpClient client = getHttpClient();
        HttpPut httpPut = new HttpPut(url);

        // add header
        if (headers != null) {
            headers.forEach(httpPut::addHeader);
        }
        httpPut.setEntity(new StringEntity(bodyJsonStr, StandardCharsets.UTF_8));
        ResultResponse resultResponse = null;
        HttpResponse response = client.execute(httpPut);
        resultResponse = buildResultResponse(response);
        return resultResponse;
    }

    /**
     * 构建有查询参数的url
     *
     * @param url     url
     * @param jsonStr json字符串
     * @return {@link String}
     */
    private static String buildParameterUrl(String url, String jsonStr) {
        // 如果paramsJsonStr不为空，则对其转换为params格式的字符串
        String encodedJsonStr = (StringUtil.isNotBlank(jsonStr)) ? convertToParams(jsonStr) : null;
        // 使用查询参数构造完整的URL
        String result = url + (StringUtil.isNotBlank(encodedJsonStr) ? (url.contains("?") ? ("&" + encodedJsonStr) : ("?" + encodedJsonStr)) : "");

        return encodedUrl(result);
    }

    /**
     * 转换为params
     *
     * @param bodyJsonStr body json-str
     * @return {@link String}
     */
    public static String convertToParams(String bodyJsonStr) {
        StringBuilder params = new StringBuilder();
        Map<String, Object> jsonMap = convertJsonToMap(bodyJsonStr);
        for (Map.Entry<String, Object> entry : jsonMap.entrySet()) {
            //排除value为空的字段
            if (StringUtil.isNullOrEmpty(entry.getValue().toString())) {
                continue;
            }
            String key = entry.getKey();
            String value = entry.getValue().toString();
            if (params.length() > 0) {
                params.append("&");
            }
            params.append(key).append("=").append(value);
        }
        return params.toString();
    }

    /**
     * 将json转换为map
     *
     * @param JsonStr json-str
     * @return {@link Map}<{@link String}, {@link Object}>
     */
    private static Map<String, Object> convertJsonToMap(String JsonStr) {
        if (StringUtil.isEmpty(JsonStr)) {
            return new HashMap<>();
        }
        Map<String, Object> jsonMap = JSONUtil.toBean(JsonStr, Map.class);
        //特殊处理options
        String optionsJson = jsonMap.get("options").toString();
        if (StringUtil.isNullOrEmpty(optionsJson)) {
            return jsonMap;
        }
        jsonMap.remove("options");
        Map<String, Object> options = JSONUtil.toBean(optionsJson, Map.class);
        jsonMap.putAll(options);
        return jsonMap;
    }

    /**
     * url编码
     *
     * @param url url
     * @return {@link String}
     */
    private static String encodedUrl(String url) {
        try {
            if (url.contains("%")) {
                // 如果URL已经编码过，直接返回原始URL
                return url;
            }
            return URLEncoder.encode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

    /**
     * 生成结果响应
     *
     * @param response 回答
     * @return {@link ResultResponse}
     */
    private static ResultResponse buildResultResponse(HttpResponse response) {
        ResultResponse resultResponse = new ResultResponse();
        resultResponse.setResult(null);

        try {
            if (response.getEntity() != null && response.getEntity().getContent() != null) {
                try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
                    StringBuilder result = new StringBuilder();
                    String line;
                    while ((line = rd.readLine()) != null) {
                        result.append(line);
                    }
                    resultResponse.setResult(result.toString());
                }
            }


            // If error, throw an exception with the response content
            if (response.getStatusLine().getStatusCode() >= 400) {
                throw new Exception(resultResponse.getResult());
            }

            // Extract and set headers
            Header[] headers = response.getAllHeaders();
            Map<String, String> headersMap = Arrays.stream(headers)
                    .collect(Collectors.toMap(Header::getName, Header::getValue));
            resultResponse.setStatus(response.getStatusLine().getStatusCode());
            resultResponse.setHeadersMap(headersMap);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        return resultResponse;
    }

    public static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] chain, String authType) {
                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] chain, String authType) {
                        }

                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
                    }
            };

            // Install the all-trusting trust manager
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            // Create an OkHttpClient that trusts all certificates
            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .build();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构造使用于Http、Https的Client
     *
     * @return HttpClient
     */
    public static CloseableHttpClient getHttpClient() {
        RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
        registryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
        // 指定信任密钥存储对象和连接套接字工厂
        try {
            // --- 需要信任连接方的 CA 证书才能正常连接，目前忽略证书验证！----
            TrustStrategy trustStrategy = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            };
            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(trustStrategy)
                    .build();
            LayeredConnectionSocketFactory sslSF =
                    new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            registryBuilder.register("https", sslSF);
        } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 设置连接管理器
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registryBuilder.build());
        connManager.setMaxTotal(500);
        connManager.setDefaultMaxPerRoute(50);

        // 构建客户端
        return HttpClientBuilder.create()
                .setDefaultRequestConfig(RequestConfig.custom()
                        // 从连接池获取到连接的超时时间，如果是非连接池的话
                        .setConnectionRequestTimeout(5 * 1000)
                        // socket中两个数据包的间隔时间
                        .setSocketTimeout(5 * 1000)
                        // 建立连接的超时时间
                        .setConnectTimeout(3 * 1000)
                        .build())
                .setKeepAliveStrategy(MY_STRATEGY)
                .setConnectionManager(connManager)
                .setDefaultRequestConfig(RequestConfig.custom().setStaleConnectionCheckEnabled(true).build())
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .build();
    }
}
